package com.geekbang;

import com.geekbang.supermarket.LittleSuperMarket;
import com.geekbang.supermarket.MerchandiseV2;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectionAppMain {
    public static void main(String... args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        LittleSuperMarket superMarket = new LittleSuperMarket("大卖场",
                "世纪大道1号", 500, 600, 100);

        MerchandiseV2 m100 = superMarket.getMerchandiseOf(100);
        MerchandiseV2 m10 = superMarket.getMerchandiseOf(10);


        // 使用反射访问属性
        // 使用反射访问方法
        // 使用反射访问静态方法和属性
        // 使用反射访问private的方法和属性


        // TODO 另一种获得Class实例的方法，直接类名点
        Class clazz = MerchandiseV2.class;

        // TODO 获取 field
        Field countField = clazz.getField("count");
        System.out.println("通过反射获取count的值："+countField.get(m100)); // TODO 等价于 m100.count

        // 获取所以的fields
//        printFields(clazz);

        // TODO 获取 private，static 的field， 并设置 accessible
        Field field = clazz.getDeclaredField("STATIC_MEMBER");
        field.setAccessible(true);
        System.out.println(field.get(null)); // 静态变量 不需要传引用，因为就一个，跟实例没关系

        // TODO 获取 method
        Method buyMethod = clazz.getMethod("buy", int.class);
        System.out.println(buyMethod.invoke(m100, 10)); // 相当于 m100.buy(10)
        System.out.println(buyMethod.invoke(m10, 10)); // 相当于 m10.buy(10)

        // TODO 获取 static method
        Method staticMethod = clazz.getMethod("getNameOf", MerchandiseV2.class);
        String str = (String) staticMethod.invoke(null, m100); // invoke 返回是一个object，要强制类型转换
        System.out.println(str);

        // TODO 获取 private method
        Method descMethod = clazz.getDeclaredMethod("describe");
        descMethod.setAccessible(true);
        descMethod.invoke(m100); // 等价于 m100.describe();
        descMethod.invoke(superMarket.getMerchandiseOf(0));
        descMethod.invoke(superMarket.getMerchandiseOf(10));

    }

    public static void printFields(Class clazz) {
        System.out.println(clazz.getName() + "里的field");
        for (Field field : clazz.getFields()) {
            System.out.println(field.getType() + " " + field.getName());
        }
    }

}
